summaryrefslogtreecommitdiff
path: root/app/[lng]
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-02 00:45:49 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-02 00:45:49 +0000
commit2acf5f8966a40c1c9a97680c8dc263ee3f1ad3d1 (patch)
treef406b5c86f563347c7fd088a85fd1a82284dc5ff /app/[lng]
parent6a9ca20deddcdcbe8495cf5a73ec7ea5f53f9b55 (diff)
(대표님/최겸) 20250702 변경사항 업데이트
Diffstat (limited to 'app/[lng]')
-rw-r--r--app/[lng]/engineering/(engineering)/report/page.tsx154
-rw-r--r--app/[lng]/evcp/(evcp)/report/page.tsx4
-rw-r--r--app/[lng]/page.tsx158
-rw-r--r--app/[lng]/procurement/(procurement)/report/page.tsx154
-rw-r--r--app/[lng]/sales/(sales)/report/page.tsx154
5 files changed, 514 insertions, 110 deletions
diff --git a/app/[lng]/engineering/(engineering)/report/page.tsx b/app/[lng]/engineering/(engineering)/report/page.tsx
index 3efaa7c3..eb932e0f 100644
--- a/app/[lng]/engineering/(engineering)/report/page.tsx
+++ b/app/[lng]/engineering/(engineering)/report/page.tsx
@@ -1,47 +1,129 @@
-import * as React from "react"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
+// app/procurement/dashboard/page.tsx
+import * as React from "react";
+import { Skeleton } from "@/components/ui/skeleton";
+import { Shell } from "@/components/shell";
+import { ErrorBoundary } from "@/components/error-boundary";
+import { getDashboardData } from "@/lib/dashboard/service";
+import { DashboardClient } from "@/lib/dashboard/dashboard-client";
+// 대시보드 데이터 로딩 컴포넌트
+async function DashboardContent() {
+ try {
+ const data = await getDashboardData("engineering");
+
+ const handleRefresh = async () => {
+ "use server";
+ return await getDashboardData("engineering");
+ };
-export default async function IndexPage() {
-
+ return (
+ <DashboardClient
+ initialData={data}
+ onRefresh={handleRefresh}
+ />
+ );
+ } catch (error) {
+ console.error("Dashboard data loading error:", error);
+ throw error;
+ }
+}
+// 대시보드 로딩 스켈레톤
+function DashboardSkeleton() {
return (
- <Shell className="gap-2">
+ <div className="space-y-6">
+ {/* 헤더 스켈레톤 */}
<div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Dashboard
- </h2>
- <p className="text-muted-foreground">
- 각종 지표 등을 대시보드로 표현하거나 리포트를 출력할 수 있습니다.
- </p>
+ <div className="space-y-2">
+ <Skeleton className="h-8 w-48" />
+ <Skeleton className="h-4 w-72" />
+ </div>
+ <Skeleton className="h-10 w-24" />
+ </div>
+
+ {/* 요약 카드 스켈레톤 */}
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
+ {[...Array(4)].map((_, i) => (
+ <div key={i} className="space-y-3 p-6 border rounded-lg">
+ <div className="flex items-center justify-between">
+ <Skeleton className="h-4 w-16" />
+ <Skeleton className="h-4 w-4" />
+ </div>
+ <Skeleton className="h-8 w-12" />
+ <Skeleton className="h-3 w-20" />
+ </div>
+ ))}
+ </div>
+
+ {/* 차트 스켈레톤 */}
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
+ {[...Array(2)].map((_, i) => (
+ <div key={i} className="space-y-4 p-6 border rounded-lg">
+ <div className="space-y-2">
+ <Skeleton className="h-6 w-32" />
+ <Skeleton className="h-4 w-48" />
+ </div>
+ <Skeleton className="h-[300px] w-full" />
+ </div>
+ ))}
+ </div>
+
+ {/* 탭 스켈레톤 */}
+ <div className="space-y-4">
+ <Skeleton className="h-10 w-64" />
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
+ {[...Array(6)].map((_, i) => (
+ <div key={i} className="space-y-4 p-6 border rounded-lg">
+ <Skeleton className="h-6 w-32" />
+ <div className="space-y-3">
+ <div className="flex justify-between">
+ <Skeleton className="h-4 w-16" />
+ <Skeleton className="h-4 w-12" />
+ </div>
+ <div className="flex gap-2">
+ <Skeleton className="h-6 w-16" />
+ <Skeleton className="h-6 w-16" />
+ <Skeleton className="h-6 w-16" />
+ </div>
+ <Skeleton className="h-2 w-full" />
+ </div>
+ </div>
+ ))}
</div>
</div>
+ </div>
+ );
+}
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
-
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
+// 에러 표시 컴포넌트
+function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
+ return (
+ <div className="flex flex-col items-center justify-center py-12 space-y-4">
+ <div className="text-center space-y-2">
+ <h3 className="text-lg font-semibold">대시보드를 불러올 수 없습니다</h3>
+ <p className="text-muted-foreground">
+ {error.message || "알 수 없는 오류가 발생했습니다."}
+ </p>
+ </div>
+ <button
+ onClick={reset}
+ className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
>
- </React.Suspense>
+ 다시 시도
+ </button>
+ </div>
+ );
+}
+
+export default async function DashboardPage() {
+ return (
+ <Shell className="gap-6">
+ <ErrorBoundary fallback={DashboardError}>
+ <React.Suspense fallback={<DashboardSkeleton />}>
+ <DashboardContent />
+ </React.Suspense>
+ </ErrorBoundary>
</Shell>
- )
-} \ No newline at end of file
+ );
+}
diff --git a/app/[lng]/evcp/(evcp)/report/page.tsx b/app/[lng]/evcp/(evcp)/report/page.tsx
index eeb6a91d..95566b05 100644
--- a/app/[lng]/evcp/(evcp)/report/page.tsx
+++ b/app/[lng]/evcp/(evcp)/report/page.tsx
@@ -10,11 +10,11 @@ import { DashboardClient } from "@/lib/dashboard/dashboard-client";
// 대시보드 데이터 로딩 컴포넌트
async function DashboardContent() {
try {
- const data = await getDashboardData("procurement");
+ const data = await getDashboardData("evcp");
const handleRefresh = async () => {
"use server";
- return await getDashboardData("procurement");
+ return await getDashboardData("evcp");
};
return (
diff --git a/app/[lng]/page.tsx b/app/[lng]/page.tsx
new file mode 100644
index 00000000..2ee83857
--- /dev/null
+++ b/app/[lng]/page.tsx
@@ -0,0 +1,158 @@
+import React from 'react';
+import Link from 'next/link';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import { ShoppingCart, Users, Settings, ArrowRight, Building2 } from 'lucide-react';
+
+export default function LandingPage() {
+ const portals = [
+ {
+ id: 'sales',
+ title: '기술영업포탈',
+ description: '기술 영업 단계에서의 RFQ를 관리할 수 있는 통합 플랫폼',
+ icon: Users,
+ color: 'from-emerald-500 to-teal-500',
+ href: '/sales',
+ features: ['벤더 관리', '기술 영업 RFQ']
+ },
+ {
+ id: 'purchase',
+ title: '구매포탈',
+ description: '협력업체에서부터 마지막 발주까지 원스톱 구매 솔루션',
+ icon: ShoppingCart,
+ color: 'from-blue-500 to-cyan-500',
+ href: '/procurement',
+ features: ['협력업체 관리', '구매관리']
+ },
+
+ {
+ id: 'design',
+ title: '설계포탈',
+ description: '벤더가 플랫폼을 통해 데이터와 문서를 제출할 수 있게 하고 TBE를 처리할 수 있는 플랫폼',
+ icon: Settings,
+ color: 'from-purple-500 to-pink-500',
+ href: '/engineering',
+ features: ['설계 기준정보관리', 'TBE']
+ }
+ ];
+
+
+
+ return (
+ <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
+ {/* Header */}
+ <header className="relative overflow-hidden">
+ <div className="absolute inset-0 bg-gradient-to-r from-blue-600/10 to-purple-600/10"></div>
+ <div className="relative container mx-auto px-4 py-16 text-center">
+ <div className="flex items-center justify-center mb-6">
+ <Building2 className="h-12 w-12 text-blue-600 mr-3" />
+ <h1 className="text-4xl md:text-6xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
+ enterprise Vendor Co-work Platform
+ </h1>
+ </div>
+ <p className="text-xl md:text-2xl text-slate-600 dark:text-slate-300 max-w-3xl mx-auto leading-relaxed">
+ 통합된 비즈니스 솔루션으로 구매부터 설계까지,
+ <br />모든 업무 프로세스를 하나의 플랫폼에서 관리하세요
+ </p>
+ <Badge variant="secondary" className="mt-6 px-4 py-2 text-sm">
+ Enterprise Ready
+ </Badge>
+ </div>
+ </header>
+
+ {/* Main Portal Selection */}
+ <main className="container mx-auto px-4 py-16">
+ <div className="text-center mb-16">
+ <h2 className="text-3xl md:text-4xl font-bold text-slate-800 dark:text-slate-100 mb-4">
+ 포털을 선택하세요
+ </h2>
+ <p className="text-lg text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
+ 각 포털은 특화된 기능과 도구를 제공하여 업무 효율성을 극대화합니다
+ </p>
+ </div>
+
+ <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
+ {portals.map((portal) => {
+ const Icon = portal.icon;
+ return (
+ <Link key={portal.id} href={portal.href} className="block">
+ <Card className="relative group cursor-pointer transition-all duration-300 hover:scale-105 hover:shadow-2xl border-0 bg-white dark:bg-slate-800 overflow-hidden h-full">
+ <div className={`absolute inset-0 bg-gradient-to-br ${portal.color} opacity-5 group-hover:opacity-10 transition-opacity duration-300`}></div>
+
+ <CardHeader className="relative pb-4">
+ <div className={`w-16 h-16 rounded-2xl bg-gradient-to-br ${portal.color} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300`}>
+ <Icon className="h-8 w-8 text-white" />
+ </div>
+ <CardTitle className="text-2xl font-bold text-slate-800 dark:text-slate-100 group-hover:text-transparent group-hover:bg-gradient-to-r group-hover:from-blue-600 group-hover:to-purple-600 group-hover:bg-clip-text transition-all duration-300">
+ {portal.title}
+ </CardTitle>
+ <CardDescription className="text-slate-600 dark:text-slate-400 text-base leading-relaxed">
+ {portal.description}
+ </CardDescription>
+ </CardHeader>
+
+ <CardContent className="relative">
+ <div className="mb-6">
+ <h4 className="font-semibold text-slate-700 dark:text-slate-300 mb-3">주요 기능</h4>
+ <div className="space-y-2">
+ {portal.features.map((feature, idx) => (
+ <div key={idx} className="flex items-center text-sm text-slate-600 dark:text-slate-400">
+ <div className={`w-2 h-2 rounded-full bg-gradient-to-r ${portal.color} mr-3`}></div>
+ {feature}
+ </div>
+ ))}
+ </div>
+ </div>
+
+ <Button className={`w-full bg-gradient-to-r ${portal.color} hover:opacity-90 text-white border-0 group-hover:shadow-lg transition-all duration-300`}>
+ 포털 접속하기
+ <ArrowRight className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform duration-300" />
+ </Button>
+ </CardContent>
+ </Card>
+ </Link>
+ );
+ })}
+ </div>
+
+ {/* Additional Info Section */}
+ <div className="mt-20 text-center">
+ <div className="bg-white dark:bg-slate-800 rounded-3xl p-8 md:p-12 shadow-xl border border-slate-200 dark:border-slate-700 max-w-4xl mx-auto">
+ <h3 className="text-2xl md:text-3xl font-bold text-slate-800 dark:text-slate-100 mb-4">
+ 모든 포털이 연동됩니다
+ </h3>
+ <p className="text-lg text-slate-600 dark:text-slate-400 mb-8">
+ 구매, 영업, 설계 포털 간의 데이터가 실시간으로 동기화되어
+ 효율적인 업무 협업이 가능합니다
+ </p>
+ <div className="flex flex-wrap justify-center gap-4">
+ <Badge variant="outline" className="px-4 py-2">실시간 동기화</Badge>
+ <Badge variant="outline" className="px-4 py-2">통합 대시보드</Badge>
+ <Badge variant="outline" className="px-4 py-2">권한 관리</Badge>
+ <Badge variant="outline" className="px-4 py-2">보안 인증</Badge>
+ </div>
+ </div>
+ </div>
+ </main>
+
+ {/* Footer */}
+ <footer className="bg-slate-800 dark:bg-slate-900 text-white py-12 mt-20">
+ <div className="container mx-auto px-4 text-center">
+ <div className="flex items-center justify-center mb-6">
+ <Building2 className="h-8 w-8 mr-2" />
+ <span className="text-xl font-semibold">enterprise Vendor Co-work Platform</span>
+ </div>
+ <p className="text-slate-400 mb-4">
+ © 2025 삼성중공업. All rights reserved.
+ </p>
+ {/* <div className="flex justify-center space-x-6 text-sm text-slate-400">
+ <Link href="/terms" className="hover:text-white transition-colors">이용약관</Link>
+ <Link href="/privacy" className="hover:text-white transition-colors">개인정보처리방침</Link>
+ <Link href="/support" className="hover:text-white transition-colors">고객지원</Link>
+ </div> */}
+ </div>
+ </footer>
+ </div>
+ );
+} \ No newline at end of file
diff --git a/app/[lng]/procurement/(procurement)/report/page.tsx b/app/[lng]/procurement/(procurement)/report/page.tsx
index 3efaa7c3..800fbd8b 100644
--- a/app/[lng]/procurement/(procurement)/report/page.tsx
+++ b/app/[lng]/procurement/(procurement)/report/page.tsx
@@ -1,47 +1,129 @@
-import * as React from "react"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
+// app/procurement/dashboard/page.tsx
+import * as React from "react";
+import { Skeleton } from "@/components/ui/skeleton";
+import { Shell } from "@/components/shell";
+import { ErrorBoundary } from "@/components/error-boundary";
+import { getDashboardData } from "@/lib/dashboard/service";
+import { DashboardClient } from "@/lib/dashboard/dashboard-client";
+// 대시보드 데이터 로딩 컴포넌트
+async function DashboardContent() {
+ try {
+ const data = await getDashboardData("procurement");
+
+ const handleRefresh = async () => {
+ "use server";
+ return await getDashboardData("procurement");
+ };
-export default async function IndexPage() {
-
+ return (
+ <DashboardClient
+ initialData={data}
+ onRefresh={handleRefresh}
+ />
+ );
+ } catch (error) {
+ console.error("Dashboard data loading error:", error);
+ throw error;
+ }
+}
+// 대시보드 로딩 스켈레톤
+function DashboardSkeleton() {
return (
- <Shell className="gap-2">
+ <div className="space-y-6">
+ {/* 헤더 스켈레톤 */}
<div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Dashboard
- </h2>
- <p className="text-muted-foreground">
- 각종 지표 등을 대시보드로 표현하거나 리포트를 출력할 수 있습니다.
- </p>
+ <div className="space-y-2">
+ <Skeleton className="h-8 w-48" />
+ <Skeleton className="h-4 w-72" />
+ </div>
+ <Skeleton className="h-10 w-24" />
+ </div>
+
+ {/* 요약 카드 스켈레톤 */}
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
+ {[...Array(4)].map((_, i) => (
+ <div key={i} className="space-y-3 p-6 border rounded-lg">
+ <div className="flex items-center justify-between">
+ <Skeleton className="h-4 w-16" />
+ <Skeleton className="h-4 w-4" />
+ </div>
+ <Skeleton className="h-8 w-12" />
+ <Skeleton className="h-3 w-20" />
+ </div>
+ ))}
+ </div>
+
+ {/* 차트 스켈레톤 */}
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
+ {[...Array(2)].map((_, i) => (
+ <div key={i} className="space-y-4 p-6 border rounded-lg">
+ <div className="space-y-2">
+ <Skeleton className="h-6 w-32" />
+ <Skeleton className="h-4 w-48" />
+ </div>
+ <Skeleton className="h-[300px] w-full" />
+ </div>
+ ))}
+ </div>
+
+ {/* 탭 스켈레톤 */}
+ <div className="space-y-4">
+ <Skeleton className="h-10 w-64" />
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
+ {[...Array(6)].map((_, i) => (
+ <div key={i} className="space-y-4 p-6 border rounded-lg">
+ <Skeleton className="h-6 w-32" />
+ <div className="space-y-3">
+ <div className="flex justify-between">
+ <Skeleton className="h-4 w-16" />
+ <Skeleton className="h-4 w-12" />
+ </div>
+ <div className="flex gap-2">
+ <Skeleton className="h-6 w-16" />
+ <Skeleton className="h-6 w-16" />
+ <Skeleton className="h-6 w-16" />
+ </div>
+ <Skeleton className="h-2 w-full" />
+ </div>
+ </div>
+ ))}
</div>
</div>
+ </div>
+ );
+}
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
-
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
+// 에러 표시 컴포넌트
+function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
+ return (
+ <div className="flex flex-col items-center justify-center py-12 space-y-4">
+ <div className="text-center space-y-2">
+ <h3 className="text-lg font-semibold">대시보드를 불러올 수 없습니다</h3>
+ <p className="text-muted-foreground">
+ {error.message || "알 수 없는 오류가 발생했습니다."}
+ </p>
+ </div>
+ <button
+ onClick={reset}
+ className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
>
- </React.Suspense>
+ 다시 시도
+ </button>
+ </div>
+ );
+}
+
+export default async function DashboardPage() {
+ return (
+ <Shell className="gap-6">
+ <ErrorBoundary fallback={DashboardError}>
+ <React.Suspense fallback={<DashboardSkeleton />}>
+ <DashboardContent />
+ </React.Suspense>
+ </ErrorBoundary>
</Shell>
- )
-} \ No newline at end of file
+ );
+}
diff --git a/app/[lng]/sales/(sales)/report/page.tsx b/app/[lng]/sales/(sales)/report/page.tsx
index 3efaa7c3..33225e33 100644
--- a/app/[lng]/sales/(sales)/report/page.tsx
+++ b/app/[lng]/sales/(sales)/report/page.tsx
@@ -1,47 +1,129 @@
-import * as React from "react"
-import { Skeleton } from "@/components/ui/skeleton"
-import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
-import { Shell } from "@/components/shell"
+// app/procurement/dashboard/page.tsx
+import * as React from "react";
+import { Skeleton } from "@/components/ui/skeleton";
+import { Shell } from "@/components/shell";
+import { ErrorBoundary } from "@/components/error-boundary";
+import { getDashboardData } from "@/lib/dashboard/service";
+import { DashboardClient } from "@/lib/dashboard/dashboard-client";
+// 대시보드 데이터 로딩 컴포넌트
+async function DashboardContent() {
+ try {
+ const data = await getDashboardData("sales");
+
+ const handleRefresh = async () => {
+ "use server";
+ return await getDashboardData("sales");
+ };
-export default async function IndexPage() {
-
+ return (
+ <DashboardClient
+ initialData={data}
+ onRefresh={handleRefresh}
+ />
+ );
+ } catch (error) {
+ console.error("Dashboard data loading error:", error);
+ throw error;
+ }
+}
+// 대시보드 로딩 스켈레톤
+function DashboardSkeleton() {
return (
- <Shell className="gap-2">
+ <div className="space-y-6">
+ {/* 헤더 스켈레톤 */}
<div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">
- Dashboard
- </h2>
- <p className="text-muted-foreground">
- 각종 지표 등을 대시보드로 표현하거나 리포트를 출력할 수 있습니다.
- </p>
+ <div className="space-y-2">
+ <Skeleton className="h-8 w-48" />
+ <Skeleton className="h-4 w-72" />
+ </div>
+ <Skeleton className="h-10 w-24" />
+ </div>
+
+ {/* 요약 카드 스켈레톤 */}
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
+ {[...Array(4)].map((_, i) => (
+ <div key={i} className="space-y-3 p-6 border rounded-lg">
+ <div className="flex items-center justify-between">
+ <Skeleton className="h-4 w-16" />
+ <Skeleton className="h-4 w-4" />
+ </div>
+ <Skeleton className="h-8 w-12" />
+ <Skeleton className="h-3 w-20" />
+ </div>
+ ))}
+ </div>
+
+ {/* 차트 스켈레톤 */}
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
+ {[...Array(2)].map((_, i) => (
+ <div key={i} className="space-y-4 p-6 border rounded-lg">
+ <div className="space-y-2">
+ <Skeleton className="h-6 w-32" />
+ <Skeleton className="h-4 w-48" />
+ </div>
+ <Skeleton className="h-[300px] w-full" />
+ </div>
+ ))}
+ </div>
+
+ {/* 탭 스켈레톤 */}
+ <div className="space-y-4">
+ <Skeleton className="h-10 w-64" />
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
+ {[...Array(6)].map((_, i) => (
+ <div key={i} className="space-y-4 p-6 border rounded-lg">
+ <Skeleton className="h-6 w-32" />
+ <div className="space-y-3">
+ <div className="flex justify-between">
+ <Skeleton className="h-4 w-16" />
+ <Skeleton className="h-4 w-12" />
+ </div>
+ <div className="flex gap-2">
+ <Skeleton className="h-6 w-16" />
+ <Skeleton className="h-6 w-16" />
+ <Skeleton className="h-6 w-16" />
+ </div>
+ <Skeleton className="h-2 w-full" />
+ </div>
+ </div>
+ ))}
</div>
</div>
+ </div>
+ );
+}
- <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
- {/* <DateRangePicker
- triggerSize="sm"
- triggerClassName="ml-auto w-56 sm:w-60"
- align="end"
- shallow={false}
- /> */}
- </React.Suspense>
-
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
+// 에러 표시 컴포넌트
+function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
+ return (
+ <div className="flex flex-col items-center justify-center py-12 space-y-4">
+ <div className="text-center space-y-2">
+ <h3 className="text-lg font-semibold">대시보드를 불러올 수 없습니다</h3>
+ <p className="text-muted-foreground">
+ {error.message || "알 수 없는 오류가 발생했습니다."}
+ </p>
+ </div>
+ <button
+ onClick={reset}
+ className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
>
- </React.Suspense>
+ 다시 시도
+ </button>
+ </div>
+ );
+}
+
+export default async function DashboardPage() {
+ return (
+ <Shell className="gap-6">
+ <ErrorBoundary fallback={DashboardError}>
+ <React.Suspense fallback={<DashboardSkeleton />}>
+ <DashboardContent />
+ </React.Suspense>
+ </ErrorBoundary>
</Shell>
- )
-} \ No newline at end of file
+ );
+}